#!/usr/bin/python
# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-

DEBUG = False
FULLSCREEN =False

try:
    import psyco
    psyco.full()
except:
    print "install psyco for better performace!"

import random
import time

import devel
import math

from pyglet import window
from pyglet.window import event
from pyglet.window import key
from pyglet.window import mouse
from pyglet import clock
from pyglet import font



import rabbyt
from rabbyt import collisions

X = 0
Y = 1

zoom_factors = [ 20, 25, 33, 50, 75, 100, 125, 133, 150, 175, 200, 400 ]


class Token( rabbyt.Sprite ):
    def __init__( self, image ):
        self.sprite = rabbyt.Sprite.__init__( self, image )
        self.imgname = image
        #self.rot = random.randint( -20, 20)

        # get mask
        rawimage = self.texture.image_data
        rawimage.format = 'A'
        rawimage.pitch = rawimage.width
        self.mask = rawimage.data
        self.mask_width = rawimage.width

        #print devel.dump( self.mask, rawimage.width)

        self.minx = - rawimage.width / 2
        self.maxx =   rawimage.width + self.minx
        self.miny = - (len(self.mask) / rawimage.width) / 2
        self.maxy =   (len(self.mask) / rawimage.width) + self.miny
        self.mask_offset = - (self.miny * rawimage.width + self.minx )

    def hit( self, point ):
        # zoom, pan, rotate point to match the zoom, position, rotation of the sprite
        rad = math.radians( -self.rot )
        cos_factor = math.cos( rad ) / self.scale
        sin_factor = math.sin( rad ) / self.scale

        # we do not round the coordinates, this way border is a little bit smaller than the entire image
        p = ( int( (point[X]-self.x) * cos_factor - (point[Y]-self.y) * sin_factor ),
              int( (point[X]-self.x) * sin_factor + (point[Y]-self.y) * cos_factor ) )

        # 'p' is the panned, rotated and scaled version of point

        # if in boundary, check if bit in mask is != 0
        if self.minx <= p[X] < self.maxx and self.miny <= p[Y] < self.maxy:
            bit = p[Y] * self.mask_width + p[X] + self.mask_offset
            return ord(self.mask[ bit ]) != 0

        # if out of boundary, returns no hit
        return False


class Deck( rabbyt.Sprite ):
    def __init__( self, folder, cards ):
        self.sprite = rabbyt.Sprite.__init__( self, "games/" + folder + "/" + cards[0] )
        self.folder = folder
        self.cards = cards

    def take( self ):
        taken = self.cards.pop( 0 )
        if self.cards:
            self.texture = "games/" + self.folder + "/" + self.cards[0]

        s = rabbyt.Sprite( "games/" + self.folder + "/" + taken )
        s.xy = self.xy
        s.bounding_radius = self.bounding_radius
        s.scale = self.scale

        return s



class Touchpanel( window.Window ):
    def __init__ ( self, size, game ):
        window.Window.__init__( self, size[X], size[Y] )
        if FULLSCREEN:
            self.set_fullscreen(True)
        if DEBUG:
            self.push_handlers(event.WindowEventLogger())

        self.set_caption( game.title )

        rabbyt.set_viewport ( size ) # any value seems to be ignored
        rabbyt.set_default_attribs()

        self.xy = (0,0)
        self.zoom = 5 # index on zoom_factors
        self.reference = None
        self.dragging = []

        self.hitter = rabbyt.Sprite(  ) # this sprite is used to find which sprite is under the mouse cursor

        self.unmovable = []
        self.movable = []
        self.deck = []
        self.dices = []


        for element in game.unmovable:
            s = rabbyt.Sprite( "games/" + game.folder + "/" + element[0] )
            s.xy = element[1]
            s.scale = zoom_factors[ self.zoom ] / 100.0
            self.unmovable.append( s )

        for element in game.decks:
            s = Deck( game.folder, element[0] )
            s.xy = element[1]
            s.scale = zoom_factors[ self.zoom ] / 100.0
            self.deck.append( s )

        for element in game.movable:
            s = Token( "games/" + game.folder + "/" + element[0] )
            s.xy = element[1]
            s.scale = zoom_factors[ self.zoom ] / 100.0
            self.movable.append( s )

        #for element in game.dices:
        #    s = Dice( "games/" + game.folder + "/" + element[0], element[2] )
        #    s.xy = element[1]
        #    s.scale = zoom_factors[ self.zoom ] / 100.0
        #    self.dices.append( s )


        # move origin to the center of the window
        self.oldsize = self.get_size()
        self.pan( (-self.oldsize[X]/2, self.oldsize[Y]/2) )

    def run ( self ):
        fps_display = clock.ClockDisplay()
        ft = font.load('Arial', 8)
        while not self.has_exit:
            clock.tick()
            self.dispatch_events()
            self.clear()
            rabbyt.clear()
            rabbyt.set_time( time.clock()*10 ) # was * 1000
            rabbyt.render_unsorted(self.unmovable)
            rabbyt.render_unsorted(self.deck)
            rabbyt.render_unsorted(self.movable)
            rabbyt.render_unsorted(self.dices)
            fps_display.draw()
            text = font.Text(ft, '%d , %d' % self.xy )
            text.draw()
            self.flip()

    def on_key_press(self, symbol, modifiers):
        if symbol == key.ESCAPE:
            self.has_exit = True

        if symbol == key.F11:
            self.set_fullscreen( not self.fullscreen )

    def on_key_release(self, symbol, modifiers):
        pass

    def on_mouse_press(self, x, y, button, modifiers):
        if button == mouse.LEFT:

            self.hitter.xy = (x,y)

            self.dragging = collisions.collide_single(self.hitter, self.movable)
            if self.dragging:
                l = len(self.movable)
                for idx in range( l ):
                    element = self.movable[l-idx-1]
                    if element in self.dragging:
                        if element.hit( self.hitter.xy ):
                            self.movable.remove( element )
                            self.movable.append( element )
                            self.dragging = [ element ]
                            return

            self.dragging = []

            deck = collisions.collide_single(self.hitter, self.deck)
            if deck:
                taken = deck[0].take()
                self.dragging = [ taken, ]
                self.movable.append( taken )
                return

            self.reference = (x,y)

        elif button == mouse.RIGHT:
            pass
        elif button == mouse.MIDDLE:
            pass

    def on_mouse_release(self, x, y, button, modifiers):
        if button == mouse.LEFT:
            self.reference = None
            self.dragging = []
        elif button == mouse.RIGHT:
            pass
        elif button == mouse.MIDDLE:
            pass

    def on_mouse_scroll(self, x, y, dx, dy):
        if dy > 0:
            self.update_zoom( 1 )
        elif dy < 0:
            self.update_zoom( -1 )

    def on_mouse_motion(self, x, y, dx, dy):
        self.xy = (x,y)

    def on_mouse_drag(self, x, y, dx, dy, button, modifiers):
        if self.reference:
            self.pan( (-dx, dy) )
        else:
            self.drag( self.dragging, ( -dx, dy ) )

    def on_resize (self, width, height):
        window.Window.on_resize( self, width, height )
        self.pan( (-(width-self.oldsize[X])/2, (height-self.oldsize[Y])/2) )
        self.oldsize = (width, height)

    def update_zoom( self, delta ):
        old_zoom = self.zoom
        self.zoom = min( len( zoom_factors) -1, max( 0, self.zoom + delta ) )

        if old_zoom == self.zoom:
            return

        old_fact = zoom_factors[ old_zoom ] / 100.0
        zoom_fact = zoom_factors[ self.zoom ] / 100.0

        ratio = zoom_fact / old_fact # ratio of the zoom difference

        # to zoom around the center of the screen we ave to pan ALL to get the center of the screen on
        # the bottom-left corner (panning in X and Y of half of the window size), then zoom (adjusting
        # pan of each sprite), then pan back ALL of the same number of pixels

        size = self.get_size()
        panx = size[X]/2
        pany = size[Y]/2

        for element in self.unmovable + self.movable + self.deck:
            element.x -= panx
            element.y -= pany

            element.scale = zoom_fact
            element.x *= ratio
            element.y *= ratio
            element.bounding_radius *= ratio

            element.x += panx
            element.y += pany

    def pan( self, delta ):
        for element in self.unmovable + self.movable + self.deck:
            element.x -= delta[X]
            element.y += delta[Y]

    def drag( self, elements, delta ):
        for element in elements:
            element.x -= delta[X]
            element.y += delta[Y]
            #element.rot += 2


if __name__ == "__main__":
    import cProfile
    from games import dune
    tp = Touchpanel( (1024,768), dune )
    #cProfile.run('tp.run()')
    tp.run()